﻿//--------------------------------------------------------------------------
// 
//  Copyright (c) Chili Software.  All rights reserved. 
// 
//  File: StringMethodsHandler.cs
//
//  Description: Converts methods to SQL compatible strings.
// 
//--------------------------------------------------------------------------

using System;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;

namespace Chili.Opf3.Linq
{
	/// <summary>
	/// Thsi class converts some methods on string into strings that are 
	/// SQL compatible and can be executed on the storage as part of a
	/// SQL query.
	/// </summary>
	public sealed class StringMethodsHandler : IMethodHandler
	{
		#region IMethodHandler Members

		/// <summary>
		/// Returns whether the class can handle the given method.
		/// </summary>
		/// <param name="method">The method to handle.</param>
		/// <param name="type">The type on which the method is defined.</param>
		public bool CanHandle(MethodInfo method, Type type)
		{
			// Check, if we support the send in methods.
			if (type == typeof(string))
			{
				switch (method.Name)
				{
					case "Contains":
					case "StartsWith":
					case "EndsWith":
						return true;
				}
			}
			return false;
		}

		/// <summary>
		/// Returns the method call converted in an SQL compatible string.
		/// </summary>
		/// <param name="expression">The expression that is converted to the SQL compatible string. This expression
		/// contains the method that has been called with all the arguments and eventually sub expressions.</param>
		/// <param name="args">Holds a utility class that contains different methods to be used during string compilation.</param>
		public string Handle(MethodCallExpression expression, MethodHandlerArgs args)
		{
			StringBuilder result = new StringBuilder();
			if (expression.Object != null)
				result.Append(args.ProcessExpression(expression.Object));
			
			// cast the first argument to a constant expression.
			ConstantExpression ce = expression.Arguments[0] as ConstantExpression;
			string value = null;
			if (ce != null)
				value = ce.Value.ToString();

			MemberExpression me = expression.Arguments[0] as MemberExpression;
			if (me != null)
			{
				// get the value of the member expression.
				object val = args.GetValue(me);
				// check it for being null. If not convert to a string.
				if (val != null)
					value = val.ToString();
			}

			// and check what method we have to handle.
			switch (expression.Method.Name)
			{
				case "Contains":
					result.Append(" like ");
					result.Append(args.AddParameter("%" + value + "%", typeof(string)));
					break;
				case "StartsWith":
					result.Append(" like ");
					result.Append(args.AddParameter(value + "%", typeof(string)));
					break;
				case "EndsWith":
					result.Append(" like ");
					result.Append(args.AddParameter("%" + value, typeof(string)));
					break;
				default:
					throw new NotSupportedException("Method " + expression.Method.Name + " is not supported.");
			}

			return result.ToString();
		}

		#endregion
	}
}
